﻿
using System.Globalization;

namespace MockingBird.Common.Infrastructure.DependencyResolution
{
    using System;
    using System.Configuration;
    using System.Diagnostics;
    using System.IO;
    using System.Threading;
    using Microsoft.Practices.Unity;
    using Microsoft.Practices.Unity.Configuration;

    /// <summary>
    /// Simplifies the dependency resolution mechanism. It is still exposing Unity as the core 
    /// mechanism but that is because this is not intended to abstract the mechanism but 
    /// to reduce redundant code in setting up the container.  
    /// </summary>
    public class DependencyResolver
    {
        /// <summary>
        /// returns the unity container to be used for all further dependency resolution
        /// and other IoC related work
        /// </summary>
        private static object _defaultContainerLockObject = new object();
        private static IUnityContainer _defaultContainer;
        private static bool isConfigFilePathDefined = false;
        private static string configFileFullName;
        private static string configFileName = "unity.config";

        /// <summary>
        /// Defines the full path to the unity.config file. 
        /// The file short name (unity.config) is assumed so users only need to provide the path.
        /// If not overridden, it assumes that it is a sibling config file 
        /// </summary>
        public static string ConfigurationFile
        {
            get
            {
                if (!isConfigFilePathDefined)
                {
                    DefineDefaultConfigFileFullName();
                    isConfigFilePathDefined = true;
                }
                return configFileFullName;
            }

            set
            {
                configFileFullName = Path.Combine(value, configFileName);
                ValidateUnityConfigFile();
                isConfigFilePathDefined = true;
            }
        }

        public static IUnityContainer DefaultContainer
        {
            get
            {
                if (null == _defaultContainer)
                {
                    lock (_defaultContainerLockObject)
                    {
                        if (null == _defaultContainer)
                        {
                            _defaultContainer = new UnityContainer();
                        }
                    }
                }
                return _defaultContainer;
            }
        }

        private static object _handlerContainerLockObject = new object();
        private static IUnityContainer _handlerContainer;
        public static IUnityContainer HandlerContainer
        {
            get
            {
                if (null == _handlerContainer)
                {
                    lock (_handlerContainerLockObject)
                    {
                        if (null == _handlerContainer)
                        {
                            _handlerContainer = DefaultContainer.CreateChildContainer();
                        }
                    }
                }
                return _defaultContainer;
            }
        }

        public static void ConfigureDefaultContainer()
        {
            Debug.WriteLine(string.Format("DependencyResolver.ConfigureDefaultContainer on thread ID: {0}", Thread.CurrentThread.ManagedThreadId));
            lock (_defaultContainerLockObject)
            {
                var section = GetConfigurationSection();
                section.Containers.Default.Configure(DefaultContainer);
            }
        }

        public static void ConfigureDefaultContainer(string name)
        {
            Debug.WriteLine(string.Format("DependencyResolver.ConfigureDefaultContainer on thread ID: {0} with name {1}", Thread.CurrentThread.ManagedThreadId, name));
            lock (_defaultContainerLockObject)
            {
                var section = GetConfigurationSection();
                section.Containers[name].Configure(DefaultContainer);
            }
        }

        public static void ConfigureHandlerContainer(string name)
        {
            Debug.WriteLine(string.Format("DependencyResolver.ConfigureHandlerContainer on thread ID: {0} with name {1}", Thread.CurrentThread.ManagedThreadId, name));
            lock (_handlerContainerLockObject)
            {
                var section = GetConfigurationSection();
                section.Containers[name].Configure(HandlerContainer);
            }
        }

        public static void ConfigureContainer(IUnityContainer container, string name)
        {
            var section = GetConfigurationSection();
            section.Containers[name].Configure(container);
        }

        private static UnityConfigurationSection GetConfigurationSection()
        {
            ExeConfigurationFileMap map = new ExeConfigurationFileMap();
            map.ExeConfigFilename = configFileFullName; // GetUnityConfigFile();

            Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);

            UnityConfigurationSection section = (UnityConfigurationSection)config.GetSection("unity");

            if (section == null)
            {
                throw new ConfigurationErrorsException(
                    String.Format(
                        "The specified unity config file {0} does not have any unity related config sections",
                        map.ExeConfigFilename));
            }

            return section;
        }

        /// <summary>
        /// Gets the path of the unity file from configuration.
        /// </summary>
        /// <returns>Full Path to the unity file.</returns>
        private static void ValidateUnityConfigFile()
        {
            if (!File.Exists(configFileFullName))
            {
                throw new FileNotFoundException("Could not locate unity config file in the path :" + configFileFullName);
            }

        }

        private static void DefineDefaultConfigFileFullName()
        {
            string applicationBasePath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
            configFileFullName = String.Format(CultureInfo.CurrentCulture, @"{0}\{1}", applicationBasePath, configFileName);

        }
    }
}
